package com.derekandbritt.booST;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

import org.opensocial.Client;
import org.opensocial.android.OpenSocialActivity;
import org.opensocial.models.Person;
import org.opensocial.providers.GoogleProvider;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.ImageView;
import android.widget.TableRow;
import android.widget.TextView;

import com.derekandbritt.booST.data.Challenge;
import com.derekandbritt.booST.data.EmotionLevel;
import com.derekandbritt.booST.data.EnergyLevel;
import com.derekandbritt.booST.data.User;
import com.derekandbritt.booST.endpoints.KokoEndpoint;
import com.derekandbritt.booST.endpoints.OpenSocialEndpoint;
import com.derekandbritt.booST.endpoints.booSTEndpoint;
import com.derekandbritt.booST.exceptions.ConnectionException;
import com.derekandbritt.booST.exceptions.DataFormatException;
import com.derekandbritt.booST.exceptions.GenericException;
import com.derekandbritt.booST.gps.GPSService;
import com.derekandbritt.booST.preferences.Preferences;
import com.derekandbritt.koko.emotion.EmotionType;

public abstract class booSTActivity extends OpenSocialActivity {

    private static final String ANDROID_SCHEME = "x-booST-opensocial-app";
	
    private static final int DIALOG_INITIALIZE 	  = 0x01;
	private static final int DIALOG_REFRESH 	  = 0x02;
	private static final int DIALOG_KOKO_TRAINING = 0x03; 
	
	private static boolean initialized = false;
	private static boolean isPaused = false;
	protected static long activeChallengeId = -1;
	
	protected final DecimalFormat decimalFormatter = new DecimalFormat("#0.00");
	
	protected OpenSocialEndpoint osEndpoint;
	protected booSTEndpoint booSTEndpoint;
	protected KokoEndpoint kokoEndpoint;
		
	protected boolean trainingMode = false;
	private Challenge trainingChallenge = null;
	private boolean closeOnDialogCompletion = false;
	
	public void onCreate(Bundle savedInstanceState) {
	    
	    // default the screen orientation to portrait mode
	    this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
	    
	    // if there is an active challenge then go to that activity
	    if(activeChallengeId > 0 && !(this instanceof ActiveChallenge)) {
	        Intent intent = new Intent(this, ActiveChallenge.class);
	        intent.putExtra(ActiveChallenge.CHALLENGE_KEY, activeChallengeId);
            startActivity(intent);
            return;
	    }
	    
	    // initialize the open social properties
	    setScheme(ANDROID_SCHEME);
	    addProvider(new GoogleProvider(), new String[] {"anonymous","anonymous"});
	    
	    // tell OpenSocialActivity to initialize
		super.onCreate(savedInstanceState);
		
		// get the OpenSocial client
		Client client = this.getClient();
		if(client == null)
		    this.showChooser();
		
		osEndpoint = new OpenSocialEndpoint(client);
		booSTEndpoint = new booSTEndpoint(osEndpoint);
		kokoEndpoint = new KokoEndpoint(osEndpoint, booSTEndpoint);
		
		// get default values out of the preferences file
		SharedPreferences prefs = getSharedPreferences(Preferences.BOOST_PREFS, 0);
        // initialize the servers
        booSTEndpoint.setHostName(prefs.getString(Preferences.BOOST_SERVER, null));
        kokoEndpoint.setHostName(prefs.getString(Preferences.KOKO_SERVER, null));
        // setup training mode
        if(prefs.getBoolean(Preferences.KOKO_TRAINING_ENABLED_PREF, false)) {
            trainingMode = true;
        }
		
		/* we cannot perform the initialization or content display step
		 * until the 3-legged OAuth activity for open social has returned. The
		 * handler will not process the message off the queue until after the
		 * current flow of execution has finished.
		 */
		Handler handler = new Handler() {
			public void handleMessage(Message msg) {
				try {
					initialize();
					if(initialized) {
						displayContent();
					}
				} catch (GenericException e) { handleError(e); }	
			}
		};
		handler.sendEmptyMessage(0);
	}
	
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
    
        if (hasFocus) {
            Client client = this.getClient();
            if(client == null)
                this.showChooser();
            else
                osEndpoint.setClient(client);
        }
    }
	
	protected void initialize() {
		if(!initialized && !isPaused)
			this.showDialog(DIALOG_INITIALIZE);
	}
	
	protected void refresh() {
		this.showDialog(DIALOG_REFRESH);
	}
	
    private void initKoko() {
    	
		// check to see if the app is registered
		try {
			if(!kokoEndpoint.isAppRegistered()) {
				kokoEndpoint.registerApp();
			}
		} catch (Exception e) {
			handleError(e, "Unable to register the application with Koko", false);
		}
		
		// check to see if the user is registered
		try {
			if(!kokoEndpoint.isUserRegistered(osEndpoint.getPersonId())) {
				Person person = osEndpoint.fetchPerson();
				kokoEndpoint.registerUser(OpenSocialEndpoint.getPersonId(person), 
				                          OpenSocialEndpoint.getFirstName(person),
				                          OpenSocialEndpoint.getLastName(person));
			}
		} catch (Exception e) {
			handleError(e, "Unable to register the user with Koko", false);
		}
		
		// check to see if the user is associated with the app
		try {
			//TODO do something with the boolean value registerAppUser returns
			kokoEndpoint.registerAppUser(osEndpoint.getPersonId());
		} catch (Exception e) {
			handleError(e, "Unable to register the user with Koko", false);
		}
    }

    private void initPreferences() {
   		
    	SharedPreferences prefs = getSharedPreferences(Preferences.BOOST_PREFS, 0);
   		
       	try {
	   		//set the user id (needed by GPS service)
	   		SharedPreferences.Editor editor = prefs.edit();
	   		editor.putString(Preferences.USER_ID_PREF, osEndpoint.getPersonId());
	   		editor.commit();

       	} catch (GenericException e) { handleError(e); }

   		//start the GPS service
		if(prefs.getBoolean(Preferences.GPS_ENABLED_PREF, false)) {
			GPSService.startGPS(this);
		}
    }
	
	private class LoadingThread extends Thread {
        
		public static final int STATUS_MESSAGE   = 0x01;
		public static final int COMPLETE_MESSAGE = 0x02;
		
		private Handler handler;
		private boolean initialize;
       
        LoadingThread(boolean initialize, Handler handler) {
        	this.initialize = initialize;
        	this.handler = handler;
        }
       
        public void run() {
        	Looper.prepare();
        	
        	Message msg = handler.obtainMessage(STATUS_MESSAGE, "Initializing Koko");
        	handler.sendMessage(msg);
        	initKoko();

        	msg = handler.obtainMessage(STATUS_MESSAGE, "Initializing Preferences");
        	handler.sendMessageDelayed(msg, 1000);
        	initPreferences();
        	
        	if(!initialize) {
                booSTEndpoint.clearCaches();
                osEndpoint.clearCaches();        	    
        	}
        	
        	if(initialize)
	        	msg = handler.obtainMessage(STATUS_MESSAGE, "Initializing Friend List");
        	else
        	    msg = handler.obtainMessage(STATUS_MESSAGE, "Refreshing Friend List");
        	handler.sendMessageDelayed(msg, 1000);
            try { booSTEndpoint.getFriends(); }
            catch (GenericException e) { handleError(e); }
        	
            if(initialize)
                msg = handler.obtainMessage(STATUS_MESSAGE, "Initializing Your Info");
            else
                msg = handler.obtainMessage(STATUS_MESSAGE, "Refreshing Your Info");
            handler.sendMessageDelayed(msg, 1000);
            try { booSTEndpoint.getUser(); }
            catch (GenericException e) { handleError(e); }
            
        	// mark the activity as initialized and dismiss the dialog
        	msg = handler.obtainMessage(COMPLETE_MESSAGE);
        	handler.sendMessageDelayed(msg, 1000);
        	initialized = true;
        }
    }
	
	private class LoadingDialog extends ProgressDialog {

		public LoadingDialog(boolean initialize, Context context) {
			super(context);
			
			//setProgressStyle(STYLE_HORIZONTAL);
			setIndeterminate(true);
			setCancelable(false);
			
			if(initialize)
				setTitle("Initializing booST");
			else
				setTitle("Refreshing booST");
			
			final Handler handler = new Handler(Looper.getMainLooper()) {
				public void handleMessage(Message msg) {
					switch(msg.what) {
						case LoadingThread.STATUS_MESSAGE:
							setMessage((String)msg.obj);
							break;
						case LoadingThread.COMPLETE_MESSAGE:
							dismiss();
				        	try { displayContent(); }
							catch (GenericException e) { handleError(e); }
							break;
					}
				}
			};
			
			LoadingThread thread = new LoadingThread(initialize, handler);
			thread.start();
		}
	}
	
   public AlertDialog getTrainingDialog(Context context) {
        final CharSequence[] items = {"Proud", "Not Proud"};

        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle("Which term most accurately describes how you feel?");
        builder.setItems(items, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int item) {
                try {
                    if(item == 0)
                        kokoEndpoint.sendEvent(trainingChallenge, EmotionType.PRIDE);
                    else
                        kokoEndpoint.sendEvent(trainingChallenge, EmotionType.SHAME);
                } catch(GenericException e) {
                    handleError(e);
                } finally {
                    if(closeOnDialogCompletion)
                        finish();
                }
            }
        });
        return builder.create();
    }
	
	@Override
	public Dialog onCreateDialog(int dialogId) {		
		Dialog dialog = null;
		switch(dialogId) {
			case DIALOG_INITIALIZE:
				dialog = new LoadingDialog(true, this);
				break;
			case DIALOG_REFRESH:
				dialog = new LoadingDialog(false, this);
				break;
			case DIALOG_KOKO_TRAINING:
			    dialog = getTrainingDialog(this);
			    break;
		}
		return dialog;
	}

	@Override
	public void onPrepareDialog(int dialogId, Dialog dialog) {
		
	}
	
	protected abstract void displayContent() throws ConnectionException, DataFormatException;

	public void onPause() {
		super.onPause();
		isPaused = true;
	}
	
	public void onResume() {
		super.onResume();
		
		if(isPaused) {
			isPaused = false;
			// attempt to initialize if the activity was paused
			// before the initialization could occur
			initialize();
			if(initialized) {
				try { handleResume(); }
				catch (GenericException e) { handleError(e); }
			}
		}
	}
	
	protected void handleResume() throws ConnectionException, DataFormatException { /* intended to be overridden */ }
	
	protected void setChallenge(Challenge challenge) throws ConnectionException, DataFormatException {
	    setChallenge(challenge, false);
	}
	
	protected void setChallenge(Challenge challenge, boolean finishOnCompletion) throws ConnectionException, DataFormatException {
	    
	    booSTEndpoint.setChallenge(challenge);
	    booSTEndpoint.refreshUser();
	    
	    if(trainingMode) {
	        trainingChallenge = challenge;
	        closeOnDialogCompletion = finishOnCompletion;
	        showDialog(DIALOG_KOKO_TRAINING);
	    }
	    else {
	        kokoEndpoint.sendEvent(challenge, null);
	        if(finishOnCompletion)
	            finish();
	    }
	}
	
	protected void configurePersonalStatus() throws ConnectionException, DataFormatException {
    	    	
    	ImageView energyChart = (ImageView)findViewById(R.id.PersonalEnergyChart);
    	TextView energyLevel = (TextView)findViewById(R.id.PersonalCurrentEnergy);
    	TextView emotionalStatus = (TextView)findViewById(R.id.PersonalEmotionalStatus);
    	
    	// get the person's associated booST user object
    	User user = booSTEndpoint.getUser();
    	
    	if(user.currentEnergy != null) {
    	    String currentEnergyLevel = decimalFormatter.format(user.currentEnergy.getLevel());
    		energyLevel.setText(this.getResources().getString(R.string.current_energy) + " " + currentEnergyLevel);
    		if(user.pastEnergy != null) {
    		    EnergyLevel[] energy = user.pastEnergy.toArray(new EnergyLevel[user.pastEnergy.size() + 1]);
    		    energy[energy.length - 1] = user.currentEnergy;
    		    energyChart.setImageDrawable(getEnergyChart(energy));
    		}
    	}
    	
    	EmotionLevel emotionLevel = user.currentEmotion;
    	String emotionValue = emotionLevel != null ? decimalFormatter.format(emotionLevel.getLevel()) + "" : "??";
    	emotionalStatus.setText(this.getResources().getString(R.string.current_emotion) + " " + emotionValue);
    	
    }
    
	protected void configureFriendsStatus(String userId) throws ConnectionException, DataFormatException {
    	    	
    	ImageView energyChart = (ImageView)findViewById(R.id.FriendEnergyChart);
    	TextView friendName = (TextView)findViewById(R.id.FriendName);
    	TextView energyLevel = (TextView)findViewById(R.id.FriendCurrentEnergy);
    	TextView emotionalStatus = (TextView)findViewById(R.id.FriendEmotionalStatus);
    	
    	String firstName = OpenSocialEndpoint.getFirstName(osEndpoint.fetchPerson(userId));
    	friendName.setText(firstName + "'s Energy");
    	
    	// get the person's associated booST user object
    	User user = booSTEndpoint.getUser(userId);
    	
    	if(user.currentEnergy != null) {
    	    String currentEnergyLevel = decimalFormatter.format(user.currentEnergy.getLevel());
    		energyLevel.setText(this.getResources().getString(R.string.current_energy) + " " + currentEnergyLevel);
    		if(user.pastEnergy != null) {
                EnergyLevel[] energy = user.pastEnergy.toArray(new EnergyLevel[user.pastEnergy.size() + 1]);
                energy[energy.length - 1] = user.currentEnergy;
                energyChart.setImageDrawable(getEnergyChart(energy));
            }
    	}
    	
    	EmotionLevel emotionLevel = user.currentEmotion;
    	String emotionValue = emotionLevel != null ? decimalFormatter.format(emotionLevel.getLevel()) + "" : "??";
    	emotionalStatus.setText(this.getResources().getString(R.string.current_emotion) + " " + emotionValue);

    }
	
    protected Drawable getEnergyChart(EnergyLevel[] energyLevels) throws DataFormatException, ConnectionException {
		try {
			
			StringBuffer data = new StringBuffer();
			for(int x = energyLevels.length; x > 0; x--) {
				if(x != energyLevels.length) {
					data.append(",");
				}
				data.append(decimalFormatter.format(energyLevels[x-1].getLevel() * 10));
			}
			
			String urlString = "http://chart.apis.google.com/chart?cht=lc&chs=100x40&chd=t:" + data + "&chxt=y&chxr=0,0,10&chf=bg,s,00000000";
			
			URL url = new URL(urlString);
			Drawable d = Drawable.createFromStream(url.openStream(), "src");
			return d;
			} catch (MalformedURLException e) {
				throw new DataFormatException("Error generating the energy chart", e);
			} catch (IOException e) {
				throw new ConnectionException("Error connecting to google charts", e);
			}
	}
	
	protected void configureChallengeDetails(Challenge challenge, String challengerId) throws DataFormatException, ConnectionException {
		
		// set challenger's name
		TextView challengerName = (TextView)findViewById(R.id.ChallengerName);
		challengerName.setText(osEndpoint.fetchPerson(challengerId).getDisplayName());
		
		// set the challenge type
		TextView type = (TextView)findViewById(R.id.ChallengeType);
		type.setText(challenge.getType().toString());
		
		// set the challenge expiration date
		SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, yyyy hh:mm aa");
		TextView expiration = (TextView)findViewById(R.id.ChallengeExpiration);
		expiration.setText(formatter.format(challenge.getTimeIssued()));
		
		// set the challenge distance
		String distanceString = challenge.getDistance() + " mile";
		if(challenge.getDistance() != 1.0) { distanceString += "s"; }
		TextView distance = (TextView)findViewById(R.id.ChallengeDistance);
		distance.setText(distanceString);
		
		// set the duration for cooperative challenges
		TableRow durationRow = (TableRow)findViewById(R.id.ChallengeTimeLimitRow);
		if(Challenge.Type.COOPERATIVE == challenge.getType()) {
			durationRow.setVisibility(View.VISIBLE);
			
			int hrs = challenge.getTimeLimit() / 60;
			int min = challenge.getTimeLimit() % 60;
			
			StringBuffer buffer = new StringBuffer();
			buffer.append(hrs + " hr");
			if(hrs != 1) { buffer.append("s"); }
			buffer.append(" " + min + " min");
			
			TextView duration = (TextView)findViewById(R.id.ChallengeTimeLimit);
			duration.setText(buffer.toString());
		} else {
			durationRow.setVisibility(View.INVISIBLE);
		}
	}
	
	protected void handleError(Exception e) {
		handleError(e, false);
	}
	
	protected void handleError(Exception e, boolean silent) {
		handleError(e, e.getMessage(), silent);
	}
	
	protected void handleError(Exception e, String message, boolean silent) {
		
		if(!silent) {
			new AlertDialog.Builder(this)
				.setTitle("Exception")
				.setMessage(message)
				.setPositiveButton("Close", null)
				.show();
		}
		e.printStackTrace();
	}
}
